﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Co_location
{
    class FoodData
    {
        static Dictionary<string, int> loc2id;
        static Dictionary<string, int> drink2id;
        static Dictionary<string, int> loc2type;
        static Dictionary<string, int> meal2label;
        static HashSet<int> alllocids;
        static bool first = true;
        static double KnownRatio = 0.75;
        static bool splitCom = false;
        static bool useCoLoc = false;
        static int preRun = 0;

        static Dictionary<int, List<MealIns>> user2meals;

        static Dictionary<int, Dictionary<DateTime, MealIns>> user2Patterns;

        static Dictionary<int, double> idf;
        static Dictionary<int, Dictionary<int, int>> tf;
        static Dictionary<int, Dictionary<int, double>> wscsmatrix;  //Weighted Spatial Cosine Similarity in 'Human Mobility, Social Ties, and Link Prediction’  paper
        
        static double[] colocmatrix;
        static Dictionary<int, SortedList<DateTime, MealIns>> user2sortedmeal;

        static Dictionary<int, string> userID2Name;
        static HashSet<int> G1 = new HashSet<int>() { 0, 11, 13, 14, 17, 20 }, G2 = new HashSet<int>() { 1, 2, 6, 10, 16, 19 }, G3 = new HashSet<int>() { 3, 4, 5, 7, 8, 9, 12, 15, 18 };
        static Dictionary<int, HashSet<int>> Groups = new Dictionary<int, HashSet<int>>() { { 1, G1 }, { 2, G2 }, { 3, G3 } };
        static Dictionary<int, ComFeature> comFeatures;
        static int maxUserNum = 0;

        public static void Setup(string[] args)
        {
            KnownRatio = double.Parse(args[0]);
            splitCom = bool.Parse(args[1]);
            if (args.Length > 2)
                useCoLoc = bool.Parse(args[2]);
            if (args.Length > 3)
                preRun = int.Parse(args[3]);
        }

        private static void PreRun()
        {
            preparation();
            user2meals = new Dictionary<int, List<MealIns>>();
            userID2Name = new Dictionary<int, string>();
            ReadJanMealTable();
            ReadMarchMealTable();
            calcWSCS();
            calcCoLoc();

            outputArff();
            outputTruth();
            outputKnownForPreRun("\\MealFeatures.csv");
            outputMatrix();
        }

        private static void outputKnownForPreRun(string truthfile)
        {
            #region outputKnown
            StreamReader reader = new StreamReader(System.IO.Directory.GetCurrentDirectory() + truthfile);
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-known.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;
            int count = 0, selected = 0;
            while ((line = reader.ReadLine()) != null)
            {
                count++;
                if ((count + preRun) % 2 == 0)
                {
                    selected++;
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    writer.WriteLine(splits[0] + "," + aggLabel(splits[splits.Length - 1]));
                }
            }
            Console.WriteLine("Konwn : " + selected);
            reader.Close();
            writer.Close();

            #endregion           
        }

        public static void Run()
        {
            if (preRun > 0)
                PreRun();
            else
            {
                preparation();
                user2meals = new Dictionary<int, List<MealIns>>();
                userID2Name = new Dictionary<int, string>();
                ReadJanMealTable();
                ReadMarchMealTable();
                calcWSCS();
                calcCoLoc();
                OutputForEx();
                outputCommuDetec();
            }
        }

        public static void ReadJanMealTable()
        {
            string rootpath = @"D:\ACM_Class\Pervasive\Food\data\JanuaryStudy";

            IEnumerator<string> folders = System.IO.Directory.EnumerateDirectories(rootpath).GetEnumerator();

            HashSet<string> drinks = new HashSet<string>();
            HashSet<string> locs = new HashSet<string>();
            HashSet<string> meals = new HashSet<string>();

            StreamWriter writer;// = new StreamWriter("D:\\Work\\Pervasive\\Food\\data\\MarchStudy\\MealFeatures.csv");
            int userid;
            while (folders.MoveNext())
            {
                string folder = folders.Current;
                StreamReader reader = new StreamReader(folder + "\\mealtable.txt");

                string line;
                userid = maxUserNum;
                user2meals.Add(userid, new List<MealIns>());
                addName(folder, userid);
                maxUserNum++;
                while ((line = reader.ReadLine()) != null)
                {
                    string[] splits = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    int tmp;

                    if (splits.Length == 0 || !int.TryParse(splits[0], out tmp))
                        continue;

                    MealIns mealinstance = new MealIns(userid, splits, folder);
                    user2meals[userid].Add(mealinstance);

                    if (!drinks.Contains(mealinstance.drinks))
                        drinks.Add(mealinstance.drinks);

                    if (!locs.Contains(mealinstance.location))
                        locs.Add(mealinstance.location);

                    if (!meals.Contains(mealinstance.dishes))
                        meals.Add(mealinstance.dishes);

                    //outputFeature(writer, mealinstance);
                }
                reader.Close();
            }
            //writer.Close();

            writer = new StreamWriter(rootpath + "\\drinks.txt");
            foreach (string dr in drinks)
                writer.WriteLine(dr);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\locs.txt");
            foreach (string loc in locs)
                writer.WriteLine(loc);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\meals.txt");
            foreach (string me in meals)
                writer.WriteLine(me);
            writer.Close();
        }

        public static void ReadMarchDishTable()
        {
            string rootpath = "D:\\Work\\Pervasive\\Food\\data\\MarchStudy";

            IEnumerator<string> folders = System.IO.Directory.EnumerateDirectories(rootpath).GetEnumerator();

            HashSet<string> dishes = new HashSet<string>();
            HashSet<string> locations = new HashSet<string>();
            HashSet<string> gps = new HashSet<string>();
            HashSet<string> wifis = new HashSet<string>();


            List<List<string>> wifiplace = new List<List<string>>();
            Dictionary<string, string> wifi2loc = new Dictionary<string, string>();

            StreamWriter sameloc = new StreamWriter("D:\\Work\\Pervasive\\Food\\data\\MarchStudy\\SameLoc.txt");
            while (folders.MoveNext())
            {
                string folder = folders.Current;
                if (!System.IO.File.Exists(folder + "\\dishtable.txt"))
                    continue;
                StreamReader reader = new StreamReader(folder + "\\dishtable.txt");

                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    string[] splits = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    int tmp;

                    if (splits.Length == 0 || !int.TryParse(splits[0], out tmp))
                        continue;

                    MealIns mealinstance = new MealIns(-1, splits, folder);

                    string oldloc;
                    if (wifi2loc.TryGetValue(mealinstance.wifi, out oldloc))
                    {
                        if (oldloc != mealinstance.location)
                        {
                            sameloc.WriteLine(oldloc + " " + mealinstance.location + " " + mealinstance.wifi + " " + folder);
                        }
                    }
                    else
                        wifi2loc.Add(mealinstance.wifi, mealinstance.location);
                    wifiplace.Add(getMac(mealinstance.wifi));
                }
                reader.Close();
            }
            Console.WriteLine(wifiplace.Count);
            sameloc.Close();

            //for (int i=0;i<wifiplace.Count;i++)
            //    for (int j = i + 1; j < wifiplace.Count; j++)
            //    {
            //        if (istheSame(wifiplace[i], wifiplace[j]))
            //            Console.WriteLine("XXXXXX");
            //    }

            //StreamWriter ww = new StreamWriter("D:\\Work\\Pervasive\\Food\\data\\MarchStudy\\Locs.txt");
            //foreach (string loc in locations)
            //    ww.WriteLine(loc);
            //ww.Close();

            //StreamWriter wr = new StreamWriter("D:\\Work\\Pervasive\\Food\\data\\MarchStudy\\Gps.txt");
            //foreach (string gp in gps)
            //    wr.WriteLine(gp);
            //wr.Close();

            //StreamWriter wi = new StreamWriter("D:\\Work\\Pervasive\\Food\\data\\MarchStudy\\Wifi.txt");
            //foreach (string wifi in wifis)
            //    wi.WriteLine(wifi);
            //wi.Close();
            ////outputLabel(dishes, rootpath);
        }

        public static void ReadMarchMealTable()
        {
            string rootpath = @"D:\ACM_Class\Pervasive\Food\data\MarchStudy";

            IEnumerator<string> folders = System.IO.Directory.EnumerateDirectories(rootpath).GetEnumerator();
            StreamWriter writer;// = new StreamWriter(rootpath + "\\MealFeatures.csv", true);

            HashSet<string> meals = new HashSet<string>();
            HashSet<string> drinks = new HashSet<string>();

            int userid;
            while (folders.MoveNext())
            {
                string folder = folders.Current;
                if (!System.IO.File.Exists(folder + "\\mealtable.txt"))
                    continue;
                StreamReader reader = new StreamReader(folder + "\\mealtable.txt");

                userid = maxUserNum;
                user2meals.Add(userid, new List<MealIns>());
                addName(folder, userid);
                maxUserNum++;

                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    string[] splits = line.Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    int tmp;

                    if (splits.Length == 0 || !int.TryParse(splits[0], out tmp))
                        continue;

                    MealIns mealinstance = new MealIns(userid, splits, folder);
                    user2meals[userid].Add(mealinstance);

                    if (!drinks.Contains(mealinstance.drinks))
                        drinks.Add(mealinstance.drinks);

                    //outputFeature(writer, mealinstance);
                    if (!meals.Contains(mealinstance.dishes))
                        meals.Add(mealinstance.dishes);
                }
                reader.Close();
            }
            //writer.Close();

            writer = new StreamWriter(@"D:\ACM_Class\Pervasive\Food\data\MarchStudy\drinks.txt");
            foreach (string dr in drinks)
                writer.WriteLine(dr);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\meals.txt");
            foreach (string meal in meals)
                writer.WriteLine(meal);
            writer.Close();
        }

        private static void readCommunityFeature()
        {
            comFeatures = new Dictionary<int, ComFeature>();
            string cPath = System.IO.Directory.GetCurrentDirectory() + "\\CommunityFeatures.csv";
            StreamReader reader = new StreamReader(cPath);
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                ComFeature cf = new ComFeature(splits);
                comFeatures.Add(cf.id, cf);
            }
            reader.Close();
        }

        private static void addName(string folder, int id)
        {
            string[] splits = folder.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            userID2Name.Add(id, splits[splits.Length - 1] + "(" + id + ")");
        }

        public static void OutputForEx()
        {
            outputArff();
            if (splitCom)
                outputComSplitTruth();
            else
                outputTruth();
            outputKnown();
            outputGraph();
            outputMatrix();
        }

        private static void preparation()
        {
            loc2id = new Dictionary<string, int>();
            string rootpath = @"D:\ACM_Class\Pervasive\Food\data";

            StreamReader locidreader = new StreamReader(rootpath + "\\MarchStudy\\loc2id.txt");
            string line;
            while ((line = locidreader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                loc2id.Add(splits[0], int.Parse(splits[1]));
            }
            locidreader.Close();

            locidreader = new StreamReader(rootpath + "\\JanuaryStudy\\loc2id.txt");
            int t;
            while ((line = locidreader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                if (!loc2id.TryGetValue(splits[0], out t))
                    loc2id.Add(splits[0], int.Parse(splits[1]));
            }
            locidreader.Close();

            alllocids = new HashSet<int>();
            foreach (int locid in loc2id.Values)
                if (!alllocids.Contains(locid))
                    alllocids.Add(locid);

            drink2id = new Dictionary<string, int>();
            StreamReader reader = new StreamReader(rootpath + "\\MarchStudy\\drink2id.txt");
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                drink2id.Add(splits[0], int.Parse(splits[1]));
            }
            reader.Close();

            loc2type = new Dictionary<string, int>();
            StreamReader tyreader = new StreamReader(rootpath + "\\MarchStudy\\loc2type.txt");
            while ((line = tyreader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                loc2type.Add(splits[0], int.Parse(splits[1]));
            }
            tyreader.Close();

            meal2label = new Dictionary<string, int>();
            StreamReader labelreader = new StreamReader(rootpath + "\\MarchStudy\\meal2label.txt");
            while ((line = labelreader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                if (meal2label.TryGetValue(splits[0], out t))
                {
                    if (t != int.Parse(splits[1]))
                        throw new Exception("Unmatched Label! " + line);
                }
                else
                    meal2label.Add(splits[0], int.Parse(splits[1]));

            }
            labelreader.Close();
        }

        private static void calcWSCS()
        {
            #region calc tf
            tf = new Dictionary<int, Dictionary<int, int>>();
            for (int user = 0; user < maxUserNum; user++)
            {
                tf.Add(user, new Dictionary<int, int>());

                foreach (int locid in alllocids)
                    tf[user].Add(locid, 0);

                foreach (MealIns meal in user2meals[user])
                    if (meal.locId != -1)
                        tf[user][meal.locId]++;
            }
            #endregion

            #region calc idf( l )
            idf = new Dictionary<int, double>();
            foreach (int loc in alllocids)
            {
                double div = 0;
                for (int user = 0; user < maxUserNum; user++)
                {
                    if (tf[user][loc] != 0)
                        div++;
                }

                idf.Add(loc, Math.Log((double)maxUserNum / div));
            }
            #endregion

            wscsmatrix = new Dictionary<int, Dictionary<int, double>>();
            for (int usera = 0; usera < maxUserNum; usera++)
            {
                List<double> vectora = new List<double>();
                foreach (int locid in alllocids)
                    vectora.Add(tf[usera][locid] * idf[locid]);

                wscsmatrix.Add(usera, new Dictionary<int, double>());
                for (int userb = 0; userb < maxUserNum; userb++)
                {
                    List<double> vectorb = new List<double>();
                    foreach (int locid in alllocids)
                        vectorb.Add(tf[userb][locid] * idf[locid]);

                    wscsmatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));
                }
                wscsmatrix[usera][usera] = 0;
            }
        }

        private static double cosineSimilarity(List<double> veca, List<double> vecb)
        {
            double scalea = 0, scaleb = 0;
            double crossproduct = 0;
            for (int i = 0; i < veca.Count; i++)
            {
                crossproduct += veca[i] * vecb[i];

                scalea += veca[i] * veca[i];
                scaleb += vecb[i] * vecb[i];
            }
            return crossproduct / (Math.Sqrt(scalea) * Math.Sqrt(scaleb));
        }

        private static void calcCoLoc()
        {
            user2sortedmeal = new Dictionary<int, SortedList<DateTime, MealIns>>();
            colocmatrix = new double[maxUserNum * maxUserNum];

            for (int user = 0; user < maxUserNum; user++)
            {
                SortedList<DateTime, MealIns> meals = new SortedList<DateTime, MealIns>();

                MealIns mealtmp;
                foreach (MealIns meal in user2meals[user])
                    if (!meals.TryGetValue(meal.start, out mealtmp))
                        meals.Add(meal.start, meal);

                user2sortedmeal.Add(user, meals);
            }

            for (int usera = 0; usera < maxUserNum; usera++)
            {
                SortedList<DateTime, MealIns> mealsA = user2sortedmeal[usera];
                for (int userb = usera; userb < maxUserNum; userb++)
                {
                    SortedList<DateTime, MealIns> mealsB = user2sortedmeal[userb];

                    #region calc the co-loc number of A and B
                    IEnumerator<KeyValuePair<DateTime, MealIns>> enumertorA = mealsA.GetEnumerator();
                    IEnumerator<KeyValuePair<DateTime, MealIns>> enumertorB = mealsB.GetEnumerator();
                    bool moveA = enumertorA.MoveNext(), moveB = enumertorB.MoveNext();
                    double coLocNum = 0;
                    double coTimeNum = 0;

                    while (moveA && moveB)
                    {
                        double timeInterval = (enumertorA.Current.Key - enumertorB.Current.Key).TotalMinutes;

                        if (Math.Abs(timeInterval) <= 10)
                        {
                            coTimeNum++;

                            if (enumertorA.Current.Value.locId == enumertorB.Current.Value.locId)
                                coLocNum++;

                            moveA = enumertorA.MoveNext();
                            moveB = enumertorB.MoveNext();
                        }
                        else if (timeInterval > 0)
                            moveB = enumertorB.MoveNext();
                        else
                            moveA = enumertorA.MoveNext();
                    }

                    //colocmatrix[usera * maxUserNum + userb] = coLocNum * 2 / (mealsA.Count + mealsB.Count);
                    colocmatrix[usera * maxUserNum + userb] = coTimeNum == 0 ? 0 : coLocNum / coTimeNum;
                    colocmatrix[userb * maxUserNum + usera] = coTimeNum == 0 ? 0 : coLocNum / coTimeNum;
                    #endregion
                }
            }
        }

        private static void outputFeature(StreamWriter writer, MealIns me)
        {
            if (first)
            {
                first = false;
                writer.WriteLine("Name,MealType,LocId,Dwell,Shared,LocType,DrinkId,Alcohol,Label");
            }

            int binaAlchol = 0;
            if (me.drinks.Contains("酒") || me.drinks.Contains("Beer") || me.drinks.Contains("beer") || me.drinks.Contains("wine"))
                binaAlchol = 1;

            int drinkid;
            if (!drink2id.TryGetValue(me.drinks, out drinkid))
                drinkid = -1;

            int loctype;
            if (!loc2type.TryGetValue(me.location, out loctype))
                loctype = -1;

            int label;
            if (!meal2label.TryGetValue(me.dishes, out label))
                label = -1;

            writer.WriteLine(getID(me) + "," + me.meal + "," + me.locId + "," + (me.dwell ? 1 : 0) + "," + (me.shared ? 1 : 0) + "," + loctype + "," + drinkid + "," + binaAlchol + "," + label);
        }

        private static bool istheSame(List<string> wifiseq1, List<string> wifiseq2)
        {
            int count = 0;
            for (int i = 0; i < wifiseq1.Count; i++)
                for (int j = 0; j < wifiseq2.Count; j++)
                {
                    if (wifiseq1[i] == wifiseq2[j])
                        count++;
                }
            if (count > 3)
                return true;
            else
                return false;
        }

        private static string getID(MealIns me)
        {
            return "U" + me.userid + "F" + me.id;
        }

        private static List<string> getMac(string wifi)
        {
            List<string> macs = new List<string>();

            string[] splits = wifi.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

            if (splits.Length == 0)
                return null;

            for (int i = 0; i < splits.Length; i++)
            {
                if (i % 2 == 1)
                {
                    macs.Add(splits[i]);
                }
            }

            return macs;
        }

        private static List<double> getPosition(string loc)
        {
            List<double> position = new List<double>();

            string[] splits = loc.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            if (splits.Length == 0)
                return null;

            double lat = double.Parse(splits[3].Substring(splits[3].IndexOf('=') + 1, splits[3].IndexOf('=') - splits[3].IndexOf('(')));
            return position;
        }

        private static void outputLabel(HashSet<string> dishes, string rootpath)
        {
            int num = 0;
            Dictionary<string, int> picked = new Dictionary<string, int>();

            StreamWriter writer = new StreamWriter(rootpath + "\\Allnoodle.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("面") || dish.Contains("粉") || dish.Contains("米线")) && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allrice.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("饭") || dish.Contains("套餐")) && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allsoup.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("粥") || dish.Contains("汤") || dish.Contains("羹")) && !dish.Contains("上汤") && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\AllDumplings.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("饺") || dish.Contains("包")) && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allchicken.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("鸡") || dish.Contains("鸭") || dish.Contains("翅") || dish.Contains("凤爪")) && !dish.Contains("鸡蛋") && !dish.Contains("鸭蛋") && !dish.Contains("鸡毛菜") && !picked.Keys.Contains(dish))
                {
                    writer.WriteLine(dish);
                    picked.Add(dish, (++num));
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allbeaf.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("羊") || dish.Contains("牛")) && !dish.Contains("牛蛙") && !dish.Contains("牛奶") && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allfish.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("鱼") || dish.Contains("牛蛙") || dish.Contains("虾") || dish.Contains("贝")) && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\Allpork.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("肉") || dish.Contains("猪") || dish.Contains("排骨") || dish.Contains("培根") || dish.Contains("肥肠") || dish.Contains("丸子")) && !dish.Contains("鸡") && !dish.Contains("牛") && !dish.Contains("鸭") && !dish.Contains("羊") && !dish.Contains("鱼")
                     && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            writer = new StreamWriter(rootpath + "\\AllForeiA.txt");
            foreach (string dish in dishes)
            {
                if ((dish.Contains("韩国") || dish.Contains("日本") || dish.Contains("寿司") || dish.Contains("拌饭") || dish.Contains("料理"))
                     && !picked.Keys.Contains(dish))
                {
                    picked.Add(dish, (++num));
                    writer.WriteLine(dish);
                }
            }
            writer.WriteLine(num);
            writer.Close();

            StreamReader reader = new StreamReader(rootpath + "\\AllVegetables.txt");
            string line;
            HashSet<string> veges = new HashSet<string>();

            while ((line = reader.ReadLine()) != null)
            {
                veges.Add(line);
            }

            writer = new StreamWriter(rootpath + "\\AllOther.txt");
            foreach (string dish in dishes)
            {
                if (!picked.Keys.Contains(dish) && !veges.Contains(dish))
                    writer.WriteLine(dish);
            }
            writer.Close();
        }

        private static void outputArff()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-schema.arff";
            StreamWriter writer = new StreamWriter(path);
            writer.WriteLine("@nodetype FoodNew");
            writer.WriteLine("@attribute Name KEY");
            if (splitCom)
                for (int i = 1; i <= 3; i++)
                {
                    writer.WriteLine(string.Format("@attribute MealType{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute LocId{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute Dwell{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute Shared{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute LocType{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute DrinkId{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute alcohol{0} CATEGORICAL", i));
                    writer.WriteLine(string.Format("@attribute Between{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Close{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute EigenVector{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Pagerank{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Clustering{0} CONTINUOUS", i));
                }
            else
            {
                writer.WriteLine("@attribute MealType CATEGORICAL");
                writer.WriteLine("@attribute LocId CATEGORICAL");
                writer.WriteLine("@attribute Dwell CATEGORICAL");
                writer.WriteLine("@attribute Shared CATEGORICAL");
                writer.WriteLine("@attribute LocType CATEGORICAL");
                writer.WriteLine("@attribute DrinkId CATEGORICAL");
                writer.WriteLine("@attribute alcohol CATEGORICAL");
            }
            writer.WriteLine("@attribute Label CATEGORICAL");
            writer.WriteLine("@nodedata foodnew-truth.csv");

            writer.WriteLine();

            writer.WriteLine("@edgetype Linked FoodNew FoodNew");
            writer.WriteLine("@Reversible");
            writer.WriteLine("@edgedata foodnew-link.rn");

            writer.Close();
        }

        private static void outputTruth()
        {
            StreamReader reader = new StreamReader(@"D:\ACM_Class\Pervasive\Food\data\GroupFast\MealFeatures.csv");
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-truth.csv";
            StreamWriter writer = new StreamWriter(path);
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < splits.Length - 1; i++)
                    writer.Write(splits[i] + ",");
                writer.WriteLine(aggLabel(splits[splits.Length - 1]));
            }
            reader.Close();
            writer.Close();
        }

        private static void outputComSplitTruth()
        {
            readCommunityFeature();
            StreamReader reader = new StreamReader(System.IO.Directory.GetCurrentDirectory() + "\\MealFeatures.csv");
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-truth.csv";
            StreamWriter writer = new StreamWriter(path);
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                int userID = getID(splits[0]);
                writer.Write(splits[0] + ",");
                for (int community = 1; community <= 3; community++)
                {
                    if (Groups[community].Contains(userID))
                    {
                        for (int i = 1; i < splits.Length - 1; i++)
                            writer.Write(splits[i] + ",");
                        outputComFeature(writer, comFeatures[userID], true);
                    }
                    else
                    {
                        for (int i = 1; i < splits.Length - 1; i++)
                            writer.Write("0,");
                        outputComFeature(writer, comFeatures[userID], false);
                    }
                }

                writer.WriteLine(aggLabel(splits[splits.Length - 1]));
            }
            reader.Close();
            writer.Close();
        }

        private static void outputComFeature(StreamWriter writer, ComFeature cf, bool belong)
        {
            if (belong)
                writer.Write(cf.Betweenness + "," + cf.Closeness + "," + cf.Eigenvector + "," + cf.PageRank + "," + cf.Clustering + ",");
            else
                writer.Write("0,0,0,0,0,");
        }

        private static void outputKnown()
        {
            StreamReader reader = new StreamReader(System.IO.Directory.GetCurrentDirectory() + "\\MealFeatures.csv");
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-known.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;
            int count = 0, selected = 0;
            Random rand = new Random();
            while ((line = reader.ReadLine()) != null)
            {
                count++;
                if (rand.NextDouble() <= KnownRatio)
                {
                    selected++;
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    writer.WriteLine(splits[0] + "," + aggLabel(splits[splits.Length - 1]));
                }
            }
            Console.WriteLine("Konwn : " + selected);
            reader.Close();
            writer.Close();
        }

        private static void outputGraph()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-link.rn";
            StreamWriter writer = new StreamWriter(path);

            int edges = 0;
            for (int i = 0; i < maxUserNum; i++)
                for (int j = i; j < maxUserNum; j++)
                {
                    if (useCoLoc)
                    {
                        if (colocmatrix[i * maxUserNum + j] < 0.001)
                            continue;
                    }
                    else
                    {
                        if (wscsmatrix[i][j] < 0.005)
                            continue;
                    }

                    foreach (MealIns mealO in user2meals[i])
                    {
                        foreach (MealIns mealT in user2meals[j])
                        {
                            if (i == j && mealO.id >= mealT.id)
                                continue;

                            //if (!G1.Contains(mealO.userid) || !G1.Contains(mealT.userid))
                            //    continue;

                            if (useCoLoc)
                                writer.WriteLine(getID(mealO) + "," + getID(mealT) + "," + colocmatrix[i * maxUserNum + j].ToString("0.00000"));
                            else
                                writer.WriteLine(getID(mealO) + "," + getID(mealT) + "," + wscsmatrix[i][j].ToString("0.00000"));
                            edges++;
                        }
                    }
                }

            Console.WriteLine("Edge Number : " + edges);
            writer.Close();
        }

        private static void outputMatrix()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-matrix.csv";
            StreamWriter writer = new StreamWriter(path);
            //writer.WriteLine("Weighted Spatial Cosine Similarity matrix: ");
            for (int i = 0; i < maxUserNum; i++)
                writer.Write("," + i);//userID2Name[i]);
            writer.WriteLine();
            
            SortedSet<double> edgeWeights = new SortedSet<double>();
            for(int userA =0;userA<maxUserNum;userA++)
            {
                for (int userB = 0; userB < maxUserNum; userB++)
                {
                    edgeWeights.Add(wscsmatrix[userA][userB]);
                }
            }
            double median = edgeWeights.ElementAt(edgeWeights.Count * 1 / 2);
            for (int i = 0; i < maxUserNum; i++)
            {
                writer.Write(i);//userID2Name[i]);
                for (int j = 0; j < maxUserNum; j++)
                {
                    if (wscsmatrix[i][j] > median)
                        writer.Write("," + wscsmatrix[i][j].ToString("0.0000"));
                    else
                        writer.Write(",0");
                }
                writer.WriteLine();
            }

            //writer.WriteLine();
            //writer.WriteLine("Co-Location Rate Matrix ");
            //for (int i = 0; i < maxUserNum; i++)
            //{
            //    for (int j = 0; j < maxUserNum; j++)
            //    {
            //        writer.Write(colocmatrix[i * maxUserNum + j].ToString("0.0000") + ",");
            //    }
            //    writer.WriteLine();
            //}

            /*for (int i = 0; i < maxUserNum; i++)
            {
                foreach (int loc in alllocids)
                {
                    writer.Write(tf[i][loc] + ",");
                }
                writer.WriteLine();
            }

            foreach (int loc in alllocids) { writer.Write(idf[loc] + ","); }
             writer.Close();
             */

            writer.Close();

            writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\G1.csv");
            for (int i = 0; i < maxUserNum; i++)
                if (G1.Contains(i))
                    writer.Write("," + userID2Name[i]);
            writer.WriteLine();

            for (int i = 0; i < maxUserNum; i++)
            {
                if (G1.Contains(i))
                {
                    writer.Write(userID2Name[i]);
                    for (int j = 0; j < maxUserNum; j++)
                        if (G1.Contains(j))
                            writer.Write("," + wscsmatrix[i][j].ToString("0.000"));

                    writer.WriteLine();
                }
            }
            writer.Close();

            writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\G2.csv");
            for (int i = 0; i < maxUserNum; i++)
                if (G2.Contains(i))
                    writer.Write("," + userID2Name[i]);
            writer.WriteLine();

            for (int i = 0; i < maxUserNum; i++)
            {
                if (G2.Contains(i))
                {
                    writer.Write(userID2Name[i]);
                    for (int j = 0; j < maxUserNum; j++)
                        if (G2.Contains(j))
                            writer.Write("," + wscsmatrix[i][j].ToString("0.000"));

                    writer.WriteLine();
                }
            }
            writer.Close();

            writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\G3.csv");
            for (int i = 0; i < maxUserNum; i++)
                if (G3.Contains(i))
                    writer.Write("," + userID2Name[i]);
            writer.WriteLine();

            for (int i = 0; i < maxUserNum; i++)
            {
                if (G3.Contains(i))
                {
                    writer.Write(userID2Name[i]);
                    for (int j = 0; j < maxUserNum; j++)
                        if (G3.Contains(j))
                            writer.Write("," + wscsmatrix[i][j].ToString("0.000"));

                    writer.WriteLine();
                }
            }
            writer.Close();
        }

        private static void outputCommuDetec()
        {
            string gPath = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-graph.txt";
            StreamWriter writer = new StreamWriter(gPath);
            for (int i = 0; i < maxUserNum; i++)
                for (int j = i; j < maxUserNum; j++)
                {
                    if (wscsmatrix[i][j] < 0.00001)
                        continue;

                    writer.WriteLine(i + " " + j);
                }
            writer.Close();
            string wPath = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-weight.weights";
            writer = new StreamWriter(wPath);
            for (int i = 0; i < maxUserNum; i++)
                for (int j = i; j < maxUserNum; j++)
                {
                    if (wscsmatrix[i][j] < 0.00001)
                        continue;

                    writer.WriteLine(i + " " + j + " " + wscsmatrix[i][j].ToString("0.0000"));
                }
            writer.Close();
        }

        private static int getID(string Name)
        {
            int uindex = Name.IndexOf('U');
            int findex = Name.IndexOf('F');
            return int.Parse(Name.Substring(uindex + 1, findex - uindex - 1));
        }

        private static int aggLabel(string l)
        {
            int old = int.Parse(l);
            return aggLabel(old);
        }

        private static int aggLabel(int old)
        {
            if (old == 1 || old == 8 || old == 9)
                return 1;

            if (old == 4 || old == 5 || old == 14 || old == 11 || old == 0)
                return 4;

            if (old == 12 || old == 2 || old == 7)
                return 2;

            if (old == 3 || old == 16)
                return 3;
            return old;
        }

        #region Pattern

        public static void PatternRun()
        {
            if (preRun > 0)
            {
                preparation();
                user2meals = new Dictionary<int, List<MealIns>>();
                userID2Name = new Dictionary<int, string>();
                ReadJanMealTable();
                ReadMarchMealTable();
                calcWSCS();
                calcCoLoc();

                outputArff();
                outputPatternTruth();
                outputKnownForPreRun("\\foodnew-truth.csv");
                if (preRun == 1)
                    outputPatternTruth();
                outputMatrix();
            }
            else
            {
                preparation();
                user2meals = new Dictionary<int, List<MealIns>>();
                userID2Name = new Dictionary<int, string>();
                ReadJanMealTable();
                ReadMarchMealTable();
                calcWSCS();
                calcCoLoc();
                outputPatternTruth();
                outputPatternKnown();                
            }
        }

        private static void generatePatternList()
        {
            user2Patterns = new Dictionary<int, Dictionary<DateTime, MealIns>>();
            Dictionary<int, SortedList<DateTime, MealIns>> sortedmeals = new Dictionary<int, SortedList<DateTime, MealIns>>();

            for (int user = 0; user < maxUserNum; user++)
            {
                SortedList<DateTime, MealIns> meals = new SortedList<DateTime, MealIns>();

                MealIns mealtmp;
                foreach (MealIns meal in user2meals[user])
                    if (!meals.TryGetValue(meal.start, out mealtmp))
                        meals.Add(meal.start, meal);

                sortedmeals.Add(user, meals);
            }

            foreach (int user in sortedmeals.Keys)
            {
                user2Patterns.Add(user, new Dictionary<DateTime, MealIns>());

                SortedList<DateTime, MealIns> meals = sortedmeals[user];
                MealIns current;
                for (int i = 0; i < meals.Count; )
                {
                    List<MealIns> tmpMeals = new List<MealIns>();
                    current = meals.ElementAt(i).Value;
                    tmpMeals.Add(current);
                    i++;
                    while (tmpMeals.Count < 3 && i < meals.Count && sameId(current.locId, meals.ElementAt(i).Value.locId))
                    {
                        current = meals.ElementAt(i).Value;
                        tmpMeals.Add(current);
                        i++;
                    }

                    MealIns m = new MealIns(tmpMeals);
                    user2Patterns[user].Add(m.start, m);
                }
            }
        }

        private static bool sameId(int id1, int id2)
        {
            if (id1 == 1)
                if (id2 == 1)
                    return true;
                else
                    return false;
            else if (id1 == 4)
                if (id2 == 4)
                    return true;
                else return false;
            else
                if (id2 == 1 || id2 == 4)
                    return false;
                else
                    return true;
        }

        private static void outputPatternTruth()
        {
            generatePatternList();

            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-truth.csv";
            StreamWriter writer = new StreamWriter(path);

            foreach (int user in user2Patterns.Keys)
            {
                foreach (MealIns meal in user2Patterns[user].Values)
                {
                    writer.Write(getID(meal) + ",");
                    if (splitCom)
                    {
                        for (int community = 1; community <= 3; community++)
                        {
                            if (Groups[community].Contains(meal.userid))
                            {
                                writer.Write(meal.meal + "," + meal.locId + "," + (meal.dwell ? 1 : 0) + "," + (meal.shared ? 1 : 0) + "," + meal.loctype + "," + meal.drinkid + "," + meal.binaAlchol + ",");
                                outputComFeature(writer, comFeatures[meal.userid], true);
                            }
                            else
                            {
                                writer.Write("0,0,0,0,0,0,0,");
                                outputComFeature(writer, comFeatures[meal.userid], false);
                            }
                        }
                    }
                    else
                        writer.Write(meal.meal + "," + meal.locId + "," + (meal.dwell ? 1 : 0) + "," + (meal.shared ? 1 : 0) + "," + meal.loctype + "," + meal.drinkid + "," + meal.binaAlchol + ",");
                    writer.WriteLine(aggLabel(meal.label.ToString()));
                }
            }
            writer.Close();
        }

        private static void outputPatternKnown()
        {
            StreamReader reader = new StreamReader(System.IO.Directory.GetCurrentDirectory() + "\\foodnew-truth.csv");
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-known.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;
            int count = 0, selected = 0;
            Random rand = new Random();
            while ((line = reader.ReadLine()) != null)
            {
                count++;
                if (rand.NextDouble() <= KnownRatio)
                {
                    selected++;
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                    writer.WriteLine(splits[0] + "," + aggLabel(splits[splits.Length - 1]));
                }
            }
            Console.WriteLine("Konwn : " + selected);
            reader.Close();
            writer.Close();
        }

        private static void outputPatternGraph()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\foodnew-link.rn";
            StreamWriter writer = new StreamWriter(path);

            int edges = 0;
            for (int i = 0; i < maxUserNum; i++)
                for (int j = i; j < maxUserNum; j++)
                {
                    if (useCoLoc)
                    {
                        if (colocmatrix[i * maxUserNum + j] < 0.001)
                            continue;
                    }
                    else
                    {
                        if (wscsmatrix[i][j] < 0.005)
                            continue;
                    }

                    foreach (MealIns mealO in user2Patterns[i].Values)
                    {
                        foreach (MealIns mealT in user2Patterns[j].Values)
                        {
                            if (i == j && mealO.id >= mealT.id)
                                continue;

                            if (useCoLoc)
                                writer.WriteLine(getID(mealO) + "," + getID(mealT) + "," + colocmatrix[i * maxUserNum + j].ToString("0.00000"));
                            else
                                writer.WriteLine(getID(mealO) + "," + getID(mealT) + "," + wscsmatrix[i][j].ToString("0.00000"));
                            edges++;
                        }
                    }
                }

            Console.WriteLine("Edge Number : " + edges);
            writer.Close();
        }

        #endregion

        struct MealIns
        {
            public int userid;
            public int id;
            public int meal; // 1 for breakfast, 2 for lunch, 3 for supper
            public DateTime start;
            public DateTime end;
            public string location; // assign a unique id for different locations.
            public bool dwell; // true for stay and flase for left
            public bool shared; // true for shared and false for solo
            public string dishes;
            public string drinks;
            public string wifi;
            public string gps;

            public int locId;

            public int loctype;
            public int drinkid;
            public int binaAlchol;
            public int label;

            public MealIns(int ui, string[] splits, string filename)
            {
                userid = ui;
                id = int.Parse(splits[0]);
                switch (splits[1])
                {
                    case "Supper":
                        meal = 3;
                        break;
                    case "Lunch":
                        meal = 2;
                        break;
                    case "Breakfast":
                        meal = 1;
                        break;
                    case "NightSnack":
                        meal = 4;
                        break;
                    default:
                        meal = 0;
                        throw new Exception(" SPLISTS One");
                }
                start = DateTime.Now;
                end = DateTime.Now;
                location = splits[4];
                dwell = (splits[5] == "Stay");
                shared = (splits[6] == "Shared");
                dishes = splits[7];
                drinks = splits[8];

                if (splits.Length >= 10)
                    wifi = splits[9];
                else
                    wifi = "Y";

                if (splits.Length >= 11)
                    gps = splits[10];
                else
                    gps = "X";

                if (!loc2id.TryGetValue(location, out locId))
                    locId = -1;
                loctype = -1;
                drinkid = -1;
                binaAlchol = -1;
                label = -1;
                start = parseTime(splits[2], filename);
                end = parseTime(splits[3], filename);
            }

            public MealIns(List<MealIns> meals)
            {
                foreach (MealIns m in meals)
                {
                    userid = m.userid;
                    id = m.id;
                    start = m.start;
                    end = m.end;
                }
                userid = meals[0].userid;
                id = meals[0].id;                
                start = meals[0].start;
                end = meals[meals.Count - 1].end;
                locId = meals[0].locId;
                
                location = "";
                dishes = drinks = wifi = gps = "";

                meal = 0;
                dwell = shared = false;
                loctype = -1;
                drinkid = -1;
                binaAlchol = -1;
                label = -1;

                List<int> ml = new List<int>();
                List<int> ba = new List<int>();
                List<int> di = new List<int>();
                List<int> lt = new List<int>();
                List<int> lb = new List<int>();
                List<bool> dwl = new List<bool>();
                List<bool> shrd = new List<bool>();
                foreach (MealIns me in meals)
                {
                    ml.Add(me.meal);

                    if (me.drinks.Contains("酒") || me.drinks.Contains("Beer") || me.drinks.Contains("beer") || me.drinks.Contains("wine"))
                        ba.Add(1);
                    else
                        ba.Add(0);

                    if (!drink2id.TryGetValue(me.drinks, out drinkid))
                        drinkid = -1;
                    di.Add(drinkid);

                    if (!loc2type.TryGetValue(me.location, out loctype))
                        loctype = -1;
                    lt.Add(loctype);

                    if (!meal2label.TryGetValue(me.dishes, out label))
                        label = -1;
                    lb.Add(label);

                    dwl.Add(me.dwell);
                    shrd.Add(me.shared);
                }
                dwell = Mode(dwl);
                shared = Mode(shrd);
                binaAlchol = Mode(ba);
                drinkid = Mode(di);
                loctype = Mode(lt);
                label = Mode(lb);
                meal = Mode(ml);
                //writer.WriteLine(getID(me) + "," + me.meal + "," + me.locId + "," + (me.dwell ? 1 : 0) + "," + (me.shared ? 1 : 0) + "," + loctype + "," + drinkid + "," + binaAlchol + "," + label);        
            }

            private bool Mode(List<bool> datas)
            {
                int tNum = 0;
                int fNum = 0;
                foreach (bool b in datas)
                    if (b)
                        tNum++;
                    else
                        fNum++;

                if (tNum >= fNum)
                    return true;
                else
                    return false;
            }

            private int Mode(List<int> datas)
            {
                Dictionary<int, int> OccurNums = new Dictionary<int, int>();
                
                foreach (int d in datas)
                {
                    if (OccurNums.Keys.Contains(d))
                        OccurNums[d]++;
                    else
                        OccurNums.Add(d, 1);
                }

                int rt = -1;
                int time = 0;
                foreach (int k in OccurNums.Keys)
                {
                    if (OccurNums[k] > time)
                    {
                        rt = k;
                        time = OccurNums[k];
                    }
                }
                return rt;
            }

            private DateTime parseTime(string s, string filename)
            {
                // s = 1/21/2011 10:06:04 AM
                string[] parts = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                string[] date = parts[0].Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                string[] time = parts[1].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);

                int plus = 0;
                try
                {
                    if (parts.Length > 2 && parts[2] == "PM" && int.Parse(time[0]) != 12)
                        plus = 12;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(filename);
                    throw new Exception(ex.ToString());
                }

                return new DateTime(int.Parse(date[2]), int.Parse(date[0]), int.Parse(date[1]), int.Parse(time[0]) + plus, int.Parse(time[1]), time.Length > 2 ? int.Parse(time[2]) : 0);
            }
        }

        struct ComFeature
        {
            public int id;
            public double Betweenness;
            public double Closeness;
            public double Eigenvector;
            public double PageRank;
            public double Clustering;

            public ComFeature(string[] splits)
            {
                id = int.Parse(splits[0]);
                Betweenness = double.Parse(splits[1]);
                Closeness = double.Parse(splits[2]);
                Eigenvector = double.Parse(splits[3]);
                PageRank = double.Parse(splits[4]);
                Clustering = double.Parse(splits[5]);
            }
        }

        #region label correlation

        public static void OutputLabelCorrel()
        {
            preparation();
            user2meals = new Dictionary<int, List<MealIns>>();
            userID2Name = new Dictionary<int, string>();
            ReadJanMealTable();
            ReadMarchMealTable();
            calcWSCS();
            calcCoLoc();

            getLabelTF();
            calcLabelWSCS();
            calcCosDis();
            calcEucLid();

            General.OutputLabelCor(wscsmatrix, label_wscsmatrix);
            General.OutputAverage(wscsmatrix, label_wscsmatrix);
            General.OutputFivePercent(label_wscsmatrix);
            General.OutputAvgEuc(eucLid, null);
        }

        static List<int> allLabels;
        static Dictionary<int, Dictionary<int, int>> label_tf;

        static Dictionary<int, Dictionary<int, double>> cosDis;
        static Dictionary<int, Dictionary<int, double>> eucLid;
        static Dictionary<int, double> label_idf;        
        static Dictionary<int, Dictionary<int, double>> label_wscsmatrix;

        private static void getLabelTF()
        {
            allLabels = new List<int>();
            label_tf = new Dictionary<int, Dictionary<int, int>>();

            foreach (var label in meal2label)
            {
                if (!allLabels.Contains(aggLabel(label.Value)))
                    allLabels.Add(aggLabel(label.Value));
            }

            for (int user = 0; user < maxUserNum; user++)
            {
                label_tf.Add(user, new Dictionary<int, int>());

                foreach (int label in allLabels)
                    label_tf[user].Add(aggLabel(label), 0);
                
                foreach (MealIns meal in user2meals[user])
                {
                    int tmpL = -1;
                    if (!meal2label.TryGetValue(meal.dishes, out tmpL))
                        continue;
                    else
                    {
                        try
                        {
                            label_tf[user][aggLabel(tmpL)]++;
                        }
                        catch (Exception)
                        {
                            Console.WriteLine("---" + aggLabel(tmpL));
                            throw;
                        }
                    }
                }
            }
        }

        private static void calcCosDis()
        {
            cosDis = new Dictionary<int, Dictionary<int, double>>();

            for (int userA = 0; userA < maxUserNum; userA++)
            {
                cosDis.Add(userA, new Dictionary<int, double>());
                
                for (int userB = 0; userB < maxUserNum; userB++)
                {
                    double dotProduct = 0;
                    double magA = 0, magB = 0;
                    foreach (var pair in label_tf[userA])
                    {
                        dotProduct += pair.Value * label_tf[userB][pair.Key];
                        magA += pair.Value * pair.Value;
                        magB += label_tf[userB][pair.Key] * label_tf[userB][pair.Key];
                    }
                    cosDis[userA].Add(userB, dotProduct / Math.Sqrt(magA * magB));
                }
                cosDis[userA][userA] = 0;
            }
        }

        private static void calcLabelWSCS()
        {
            #region calc idf
            label_idf = new Dictionary<int, double>();
            foreach (int label in allLabels)
            {
                double div = 0;
                for (int user = 0; user < maxUserNum; user++)
                {
                    if (label_tf[user][label] != 0)
                        div++;
                }                
                label_idf.Add(label, Math.Log((double)maxUserNum / div));                
            }
            #endregion

            #region calcwscs
            label_wscsmatrix = new Dictionary<int, Dictionary<int, double>>();
            for (int usera = 0; usera < maxUserNum; usera++)
            {
                List<double> vectora = new List<double>();
                foreach (int label in allLabels)
                    vectora.Add(label_tf[usera][label] * label_idf[label]);

                label_wscsmatrix.Add(usera, new Dictionary<int, double>());
                for (int userb = 0; userb < maxUserNum; userb++)
                {
                    List<double> vectorb = new List<double>();
                    foreach (int label in allLabels)
                        vectorb.Add(label_tf[userb][label] * label_idf[label]);

                    label_wscsmatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));                   
                }
                label_wscsmatrix[usera][usera] = 0;
            }
            #endregion
        }

        private static void calcEucLid()
        {
            eucLid = new Dictionary<int, Dictionary<int, double>>();

            for (int userA = 0; userA < maxUserNum; userA++)
            {
                eucLid.Add(userA, new Dictionary<int, double>());

                for (int userB = 0; userB < maxUserNum; userB++)
                {
                    double sum = 0;                    
                    foreach (var pair in label_tf[userA])
                    {
                        sum += (pair.Value - label_tf[userB][pair.Key]) * (pair.Value - label_tf[userB][pair.Key]);
                    }
                    eucLid[userA].Add(userB, Math.Sqrt(sum));
                }                
            }
        }
        #endregion
    }
}
